package com.allen.app.common.config.aspect;

import cn.hutool.core.date.TimeInterval;
import com.allen.app.common.utils.CheckObjectUtils;
import java.lang.reflect.Method;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ObjectUtils;

@Slf4j
@Aspect
public class DistributedLockAspect {

    private RedissonClient redisson;

    public DistributedLockAspect(RedissonClient redisson) {
        this.redisson = redisson;
    }

    @Around("@annotation(distributedLock)")
    public Object around(ProceedingJoinPoint pjp, DistributedLock distributedLock) throws Throwable {
        //如果开关未开启,不执行aop处理
        if (Objects.isNull(distributedLock) || !distributedLock.enabled()) {
            return pjp.proceed();
        }
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        String name = AcpectCommonUtils.parseKey(distributedLock.name(), method, pjp.getArgs());
        name = "aspect:DistributedLock:"+name;
        log.info("DistributedLockAspect start:\t{} ", name);
        RLock lock = this.getLock(name, distributedLock);
        if (!lock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), distributedLock.timeUnit())) {
            log.warn("get lock failed [{}]", name);
            return null;
        }

        try {
            return pjp.proceed();

        } finally {
            lock.unlock();
            log.debug("release lock [{}]", name);
        }
    }

    private RLock getLock(String name, DistributedLock distributedLock) {
        switch (distributedLock.type()) {
            case REENTRANT_LOCK:
                return redisson.getLock(name);
            case FAIR_LOCK:
                return redisson.getFairLock(name);
            case READ_LOCK:
                return redisson.getReadWriteLock(name).readLock();
            case WRITE_LOCK:
                return redisson.getReadWriteLock(name).writeLock();
            default:
                throw new RuntimeException("do not support lock type: " + distributedLock.type().name());
        }
    }
}
